﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Principal;
using System.Text;

using Koossery.MVCwin.Actions.impl;
using Koossery.MVCwin.Filters.basefilter;
using Koossery.MVCwin.Filters.context;
using Koossery.MVCwin.Filters.itf;
using Koossery.MVCwin.Properties;
using Koossery.MVCwin.Util;

namespace Koossery.MVCwin.Filters.impl
{
    /// <author>Mohamadou Yacoubou</author>
    /// <summary>
    /// The Authorization filter attribute. It's executed before any other filter
    /// </summary>
	public class AuthorizationFilterAttribute : FilterAttribute, IAuthorizationFilter
	{
		#region Fields

        /// <summary>
        /// Set of roles to check
        /// </summary>
		private string _roles;
		private string[] _rolesSplit = new string[0];
        /// <summary>
        /// Set of users to check
        /// </summary>
		private string _users;
		private string[] _usersSplit = new string[0];

		#endregion Fields

		#region Public Properties

		public string Roles
		{
			get
			{
			    return _roles ?? String.Empty;
			}
			set
			{
			    _roles = value;
			    _rolesSplit = SplitString(value);
			}
		}

		public string Users
		{
			get
			{
			    return _users ?? String.Empty;
			}
			set
			{
			    _users = value;
			    _usersSplit = SplitString(value);
			}
		}

		#endregion Public Properties

		#region Protected Methods

        /// <summary>
        /// The Authorization Core encapsulate the authorization process.
        /// </summary>
        /// <param name="filterContext">The filter context.</param>
        /// <returns><c>true</c> if the user is authenticated and authorized; otherwise, <c>false</c>.</returns>
		protected virtual bool AuthorizeCore(AuthorizationContext filterContext)
		{
			if (filterContext == null)
			{
			    throw new ArgumentNullException(Resources.ArgumentNullException);
			}

			//Retrieving the currentUser
			IPrincipal user = Constants.CurrentUser;

			//Check the authentication
            if (user == null || !user.Identity.IsAuthenticated)
            {
                filterContext.IsAuthenticated = false;
                return false;
            }

			//Check if the autorizations
            filterContext.IsAuthorized = false;
            if (_usersSplit.Length > 0 && !_usersSplit.Contains(user.Identity.Name, StringComparer.OrdinalIgnoreCase)) return false;
            

			if (_rolesSplit.Length > 0 && !_rolesSplit.Contains((user.Identity as CustomIdentity).Role)) return false;

			//The user is authenticated and autorized
            filterContext.IsAuthorized = true;
			return true;
		}

		#endregion Protected Methods

		#region Internal Methods

        /// <summary>
        /// Splits a specific string using the , as separator.
        /// </summary>
        /// <param name="original">The original.</param>
        /// <returns></returns>
		internal static string[] SplitString(string original)
		{
			if (String.IsNullOrEmpty(original))
			{
			    return new string[0];
			}

			var split = from piece in original.Split(',')
			            let trimmed = piece.Trim()
			            where !String.IsNullOrEmpty(trimmed)
			            select trimmed;
			return split.ToArray();
		}

		#endregion Internal Methods

		#region Public Methods

        /// <summary>
        /// The unique method that is executed before all other filters
        /// </summary>
        /// <param name="filterContext">The context of execution</param>
		public virtual void OnAuthorization(Koossery.MVCwin.Filters.context.AuthorizationContext filterContext)
		{
			if (filterContext == null) throw new ArgumentNullException(Resources.ArgumentNullException);
			if (AuthorizeCore(filterContext)) filterContext.Result = null;
			else
			{
                //Check if the controller type is correct
                if (ControllerType != null)
                {
                    filterContext.Result = new RedirectToActionResult(ControllerType.Name, this.ActionName);
                }
                else
                {
                    if (this.ViewName != null)
                    {
                        filterContext.Result = new ViewResult(this.ViewName);
                    }
                    else throw new Exception(Resources.ArgumentMissingException);
                }
			}
		}

		#endregion Public Methods
	}
}